﻿#region License

// This file is part of Grawlix.
// 
// Grawlix is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
// Grawlix is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with Grawlix.  If not, see
// <http://www.gnu.org/licenses/>.

#endregion

using System;

namespace Grawlix
{
	/// <summary>
	/// Automatically implements RAII-type acquisition based upon
	/// the given state.
	/// </summary>
	public interface IBindable
	{
		/// <summary>
		/// Binds this instance.
		/// </summary>
		void Bind();

		/// <summary>
		/// Unbinds this instance.
		/// </summary>
		void Unbind();
	}

	/// <summary>
	/// Implicit use extensions for the <see cref="IBindable"/>
	/// type.
	/// </summary>
	public static class BindableImplicitUseExtensions
	{
		/// <summary>
		/// Binds and returns a binder for a using block.
		/// </summary>
		/// <param name="bindable">The bindable object.</param>
		/// <returns>The new binder which can be disposed to release
		/// resources.</returns>
		public static Binder<T> Use<T>(this T bindable)
			where T : IBindable
		{
			return new Binder<T>(bindable);
		}
	}

	/// <summary>
	/// An internally-used structure for disposing of bound types.
	/// </summary>
	/// <typeparam name="T">The <see cref="IDisposable"/>
	/// implementing structure type.
	/// </typeparam>
	public struct Binder<T> : IDisposable,
	                          IEquatable<Binder<T>>
		where T : IBindable
	{
		/// <summary>
		/// The bindable object.
		/// </summary>
		private readonly T _bindable;

		/// <summary>
		/// Initializes a new instance of the <see cref="Binder{T}"/> struct.
		/// </summary>
		/// <param name="bindable">The bindable object.</param>
		public Binder(T bindable)
		{
			_bindable = bindable;
			bindable.Bind();
		}

		/// <summary>
		/// Unbinds the bindable object.
		/// </summary>
		public void Dispose()
		{
			_bindable.Unbind();
		}

		/// <summary>
		/// Indicates whether the current object is equal to another
		/// object of the same type.
		/// </summary>
		/// <param name="other">An object to compare with this object.
		/// </param>
		/// <returns>
		/// true if the current object is equal to the 
		/// <paramref name="other"/> parameter; otherwise, false.
		/// </returns>
		public bool Equals(Binder<T> other)
		{
			return Equals(other._bindable, _bindable);
		}

		/// <summary>
		/// Indicates whether this instance and a specified object are
		/// equal.
		/// </summary>
		/// <param name="obj">Another object to compare to.</param>
		/// <returns>
		/// true if <paramref name="obj"/> and this instance are the
		/// same type and represent the same value; otherwise, false.
		/// </returns>
		public override bool Equals(object obj)
		{
			if (obj.GetType() != typeof(Binder<T>))
				return false;
			return Equals((Binder<T>)obj);
		}

		/// <summary>
		/// Returns the hash code for this instance.
		/// </summary>
		/// <returns>
		/// A 32-bit signed integer that is the hash code for this
		/// instance.
		/// </returns>
		public override int GetHashCode()
		{
			return _bindable.GetHashCode();
		}

		/// <summary>
		/// Compares two <see cref="Binder{T}"/> structures for
		/// equality.
		/// </summary>
		/// <param name="left">The left-hand side of the comparison.
		/// </param>
		/// <param name="right">The right-hand side of the comparison.
		/// </param>
		/// <returns><c>true</c> if equal; else <c>false</c>.</returns>
		public static bool operator ==(
			Binder<T> left, Binder<T> right)
		{
			return left.Equals(right);
		}

		/// <summary>
		/// Compares two <see cref="Binder{T}"/> structures for
		/// inequality.
		/// </summary>
		/// <param name="left">The left-hand side of the comparison.
		/// </param>
		/// <param name="right">The right-hand side of the comparison.
		/// </param>
		/// <returns><c>false</c> if equal; else <c>true</c>.</returns>
		public static bool operator !=(
			Binder<T> left, Binder<T> right)
		{
			return !left.Equals(right);
		}
	}
}